
from blinker import Signal
from src.Core.GraphBase import GraphBase
from src.Core.Common import *
from src.Core import version

# 定义根图的名称
ROOT_GRAPH_NAME = "root"


class GraphManager(object):
    """
    用于存储图树的数据结构。

    该类负责切换活动图，可在图树中插入或移除图，能在所有图中搜索节点和变量。
    同时，该类还负责生成唯一的名称。
    """

    def __init__(self):
        """
        初始化图管理器。
        """
        super(GraphManager, self).__init__()
        # 仅用于命令行界面，标记是否请求终止操作
        self.terminationRequested = False
        # 当图发生变化时发出的信号，携带变化的图对象
        self.graphChanged = Signal(object)
        # 存储图的字典，键为图的唯一标识符，值为图对象
        self._graphs = {}
        # 当前活动的图对象，初始为 None
        self._activeGraph = None
        # 创建根图并将其设置为活动图
        self._activeGraph = GraphBase(ROOT_GRAPH_NAME, self)
        # 标记该图为根图
        self._activeGraph.setIsRoot(True)

    def findRootGraph(self):
        """
        返回顶层的根图。

        :rtype: :class:`~PyFlow.Core.GraphBase.GraphBase`
        """
        # 存储根图的列表
        roots = []
        # 遍历所有图，找出根图
        for graph in self.getAllGraphs():
            if graph.isRoot():
                roots.append(graph)
        # 确保只有一个根图，否则抛出断言错误
        assert len(roots) == 1, "Fatal! Multiple roots!"
        return roots[0]

    def selectRootGraph(self):
        """
        选择根图作为活动图。
        """
        self.selectGraph(self.findRootGraph())

    def serialize(self):
        """
        将自身序列化为 JSON 格式的数据。

        所有子图都会被序列化。

        :rtype: dict
        """
        # 找到根图
        rootGraph = self.findRootGraph()
        # 序列化根图
        saved = rootGraph.serialize()
        # 在序列化数据中添加文件版本信息
        saved["fileVersion"] = str(version.currentVersion())
        # 在序列化数据中添加当前活动图的名称
        saved["activeGraph"] = self.activeGraph().name
        return saved

    def removeGraphByName(self, name):
        """
        根据图的名称 :attr:`~PyFlow.Core.GraphBase.GraphBase.name` 移除图。

        :param name: 要移除的图的名称
        :type name: str
        """
        # 根据名称查找图
        graph = self.findGraph(name)
        if graph is not None:
            # 清空图中的内容
            graph.clear()
            # 从存储图的字典中移除该图
            self._graphs.pop(graph.uid)
            if graph.parentGraph is not None:
                if graph in graph.parentGraph.childGraphs:
                    # 从父图的子图列表中移除该图
                    graph.parentGraph.childGraphs.remove(graph)
            # 删除图对象
            del graph

    def removeGraph(self, graph):
        """
        移除指定的图。

        :param graph: 要移除的图
        :type graph: :class:`~PyFlow.Core.GraphBase.GraphBase`
        """
        if graph.uid in self._graphs:
            # 清空图中的内容
            graph.clear()
            # 从存储图的字典中移除该图
            self._graphs.pop(graph.uid)
            if graph.parentGraph is not None:
                if graph in graph.parentGraph.childGraphs:
                    # 从父图的子图列表中移除该图
                    graph.parentGraph.childGraphs.remove(graph)
            # 删除图对象
            del graph

    def deserialize(self, data):
        """
        根据序列化的数据填充自身。

        :param data: 序列化的数据
        :type data: dict
        """
        if "fileVersion" in data:
            # 从序列化数据中获取文件版本信息
            fileVersion = version.Version.fromString(data["fileVersion"])  # TODO: find purpose
        else:
            # 处理旧版本数据
            pass
        # 清空图管理器，不保留根图
        self.clear(keepRoot=False)
        # 创建新的根图
        self._activeGraph = GraphBase(str("root"), self)
        # 根据序列化数据填充根图
        self._activeGraph.populateFromJson(data)
        # 标记该图为根图
        self._activeGraph.setIsRoot(True)
        # 选择该图作为活动图
        self.selectGraph(self._activeGraph)

    def clear(self, keepRoot=True, *args, **kwargs):
        """
        清空所有内容。

        :param keepRoot: 是否保留根图
        :type keepRoot: bool
        """
        # 选择根图作为活动图
        self.selectGraphByName(ROOT_GRAPH_NAME)
        # 根据名称移除根图
        self.removeGraphByName(ROOT_GRAPH_NAME)
        # 清空存储图的字典
        self._graphs.clear()
        self._graphs = {}
        # 删除当前活动图对象
        del self._activeGraph
        self._activeGraph = None
        if keepRoot:
            # 如果需要保留根图，则创建新的根图并设置为活动图
            self._activeGraph = GraphBase(ROOT_GRAPH_NAME, self)
            self.selectGraph(self._activeGraph)
            self._activeGraph.setIsRoot(True)

    def Tick(self, deltaTime):
        """
        定期调用所有图的 :meth:`~PyFlow.Core.GraphBase.GraphBase.Tick` 方法。

        :param deltaTime: 距离上次调用的时间间隔
        :type deltaTime: float
        """
        # 遍历所有图，调用其 Tick 方法
        for graph in self._graphs.values():
            graph.Tick(deltaTime)

    def findVariableRefs(self, variable):
        """
        返回在所有图中生成的指定变量访问器的列表。

        :param variable: 要搜索访问器的变量
        :type variable: :class:`~PyFlow.Core.Variable.Variable`
        :rtype: list(:class:`~PyFlow.Core.NodeBase.NodeBase`)
        """
        result = []
        # 遍历所有 getVar 和 setVar 节点
        for node in self.getAllNodes(classNameFilters=["getVar", "setVar"]):
            if node.variableUid() == variable.uid:
                # 如果节点的变量唯一标识符与指定变量的一致，则添加到结果列表中
                result.append(node)
        return result

    def findGraph(self, name):
        """
        尝试根据图的名称 :attr:`~PyFlow.Core.GraphBase.GraphBase.name` 查找图。

        :param name: 目标图的名称
        :type name: str
        :rtype: :class:`~PyFlow.Core.GraphBase.GraphBase` 或 None
        """
        # 获取图名称与图对象的字典
        graphs = self.getGraphsDict()
        if name in graphs:
            return graphs[name]
        return None

    def findPinByName(self, pinFullName):
        """
        尝试在所有图中根据引脚名称查找引脚。

        :param pinFullName: 引脚的完整名称，包括节点命名空间
        :type pinFullName: str
        :rtype: :class:`~PyFlow.Core.PinBase.PinBase` 或 None
        """
        result = None
        # 遍历所有图，查找引脚
        for graph in self.getAllGraphs():
            result = graph.findPin(pinFullName)
            if result is not None:
                break
        return result

    def findNode(self, name):
        """
        在所有图中查找指定名称的节点。

        :param name: 要搜索的节点名称
        :type name: str
        :rtype: :class:`~PyFlow.Core.NodeBase.NodeBase`
        """
        result = None
        # 遍历所有图，查找节点
        for graph in self.getAllGraphs():
            result = graph.findNode(name)
            if result is not None:
                break
        return result

    def findVariableByUid(self, uuid):
        """
        在所有图中根据变量的唯一标识符查找变量。

        :param uuid: 变量的唯一标识符
        :type uuid: :class:`~uuid.UUID`
        :rtype: :class:`~PyFlow.Core.Variable.Variable` 或 None
        """
        result = None
        # 遍历所有图中的变量
        for graph in self._graphs.values():
            if uuid in graph.getVars():
                result = graph.getVars()[uuid]
                break
        return result

    def findVariableByName(self, name):
        """
        在所有图中根据变量名称查找变量。

        :param name: 变量名称
        :type name: str
        :rtype: :class:`~PyFlow.Core.Variable.Variable` 或 None
        """
        # 遍历所有图中的变量
        for graph in self._graphs.values():
            for var in graph.getVars().values():
                if var.name == name:
                    return var
        return None

    def location(self):
        """
        返回当前活动图的位置。

        .. seealso ::

            :meth:`PyFlow.Core.GraphBase.GraphBase.location`
        """
        return self.activeGraph().location()

    def getGraphsDict(self):
        """
        创建并返回一个字典，其中图的名称与图对象关联。

        :rtype: dict(str, :class:`~PyFlow.Core.GraphBase.GraphBase`)
        """
        result = {}
        # 遍历所有图，将图名称和图对象添加到字典中
        for graph in self.getAllGraphs():
            result[graph.name] = graph
        return result

    def add(self, graph):
        """
        将图添加到存储中，并确保图的名称是唯一的。

        :param graph: 要添加的图
        :type graph: :class:`~PyFlow.Core.GraphBase.GraphBase`
        """
        # 为图生成唯一的名称
        graph.name = self.getUniqGraphName(graph.name)
        # 将图添加到存储图的字典中
        self._graphs[graph.uid] = graph

    def activeGraph(self):
        """
        返回当前活动的图。

        :rtype: :class:`~PyFlow.Core.GraphBase.GraphBase`
        """
        return self._activeGraph

    def selectGraphByName(self, name):
        """
        根据图的名称设置活动图，并触发图变化事件。

        :param name: 目标图的名称
        :type name: str
        """
        # 获取图名称与图对象的字典
        graphs = self.getGraphsDict()
        if name in graphs:
            if name != self.activeGraph().name:
                # 获取目标图
                newGraph = graphs[name]
                # 设置新的活动图
                self._activeGraph = newGraph
                # 发出图变化信号
                self.graphChanged.send(self.activeGraph())

    def selectGraph(self, graph):
        """
        将指定的图设置为活动图，并触发图变化事件。

        :param graph: 目标图
        :type graph: :class:`~PyFlow.Core.GraphBase.GraphBase`
        """
        # 遍历所有图，找到指定名称的图
        for newGraph in self.getAllGraphs():
            if newGraph.name == graph.name:
                if newGraph.name != self.activeGraph().name:
                    # 设置新的活动图
                    self._activeGraph = newGraph
                    # 发出图变化信号
                    self.graphChanged.send(self.activeGraph())
                    break

    def getAllGraphs(self):
        """
        返回所有的图。

        :rtype: list(:class:`~PyFlow.Core.GraphBase.GraphBase`)
        """
        return [g for g in self._graphs.values()]

    def getAllNodes(self, classNameFilters=None):
        """
        返回所有图中的所有节点。

        :param classNameFilters: 如果指定了类名过滤器，则只考虑这些节点类
        :type classNameFilters: list(str)
        :rtype: list(:class:`~PyFlow.Core.NodeBase.NodeBase`)
        """
        if classNameFilters is None:
            classNameFilters = []
        allNodes = []
        # 遍历所有图，获取节点
        for graph in self.getAllGraphs():
            if len(classNameFilters) == 0:
                allNodes.extend(list(graph.getNodes().values()))
            else:
                allNodes.extend(
                    [
                        node
                        for node in graph.getNodes().values()
                        if node.__class__.__name__ in classNameFilters
                    ]
                )
        return allNodes

    def getAllVariables(self):
        """
        返回所有变量的列表。

        :rtype: list(:class:`~PyFlow.Core.Variable.Variable`)
        """
        result = []
        # 遍历所有图，获取变量
        for graph in self.getAllGraphs():
            result.extend(list(graph.getVars().values()))
        return result

    @staticmethod
    def getUniqGraphPinName(graph, name):
        """
        为图返回唯一的引脚名称。

        由复合节点和 graphInputs、graphOutputs 节点使用，确保所有暴露给复合节点的引脚名称唯一。

        :param graph: 目标图
        :type graph: :class:`~PyFlow.Core.GraphBase.GraphBase`
        :param name: 目标引脚名称
        :type name: str

        :rtype: str
        """
        existingNames = []
        # 遍历 graphInputs 和 graphOutputs 节点，获取现有引脚名称
        for node in graph.getNodesList(
            classNameFilters=["graphInputs", "graphOutputs"]
        ):
            existingNames.extend([pin.name for pin in node.pins])
        # 从现有名称列表中生成唯一名称
        return getUniqNameFromList(existingNames, name)

    def getAllNames(self):
        """
        返回所有已注册名称的列表。

        包括图、节点、引脚、变量的名称。

        :rtype: list(str)
        """
        existingNames = [g.name for g in self.getAllGraphs()]
        existingNames.extend([n.name for n in self.getAllNodes()])
        existingNames.extend([var.name for var in self.getAllVariables()])
        # 遍历所有节点，获取引脚名称
        for node in self.getAllNodes():
            existingNames.extend([pin.name for pin in node.pins])
        return existingNames

    def getUniqName(self, name):
        """
        返回唯一的名称。

        :param name: 源名称
        :type name: str
        :rtype: str
        """
        # 获取所有已注册的名称
        existingNames = self.getAllNames()
        # 从现有名称列表中生成唯一名称
        return getUniqNameFromList(existingNames, name)

    def getUniqGraphName(self, name):
        """
        返回唯一的图名称。

        :param name: 源名称
        :type name: str
        :rtype: str
        """
        # 获取所有图的名称
        existingNames = [g.name for g in self.getAllGraphs()]
        # 从现有名称列表中生成唯一名称
        return getUniqNameFromList(existingNames, name)

    def getUniqNodeName(self, name):
        """
        返回唯一的节点名称。

        :param name: 源名称
        :type name: str
        :rtype: str
        """
        # 获取所有节点的名称
        existingNames = [n.name for n in self.getAllNodes()]
        # 从现有名称列表中生成唯一名称
        return getUniqNameFromList(existingNames, name)

    def getUniqVariableName(self, name):
        """
        返回唯一的变量名称。

        :param name: 源名称
        :type name: str
        :rtype: str
        """
        # 获取所有变量的名称
        existingNames = [var.name for var in self.getAllVariables()]
        # 从现有名称列表中生成唯一名称
        return getUniqNameFromList(existingNames, name)

    def plot(self):
        """
        将所有数据打印到控制台，可能对调试有用。
        """
        # 找到根图
        root = self.findRootGraph()
        print(
            "Active graph: {0}".format(str(self.activeGraph().name)),
            "All graphs:",
            [g.name for g in self._graphs.values()],
        )
        # 打印根图的信息
        root.plot()


@SingletonDecorator
class GraphManagerSingleton(object):
    """
    单例类，内部持有图管理器的实例。应用程序将其作为主图管理器使用。
    """

    def __init__(self):
        """
        初始化单例类，创建图管理器实例。
        """
        self.man = GraphManager()

    def get(self):
        """
        返回图管理器的实例。

        :rtype: :class:`~PyFlow.Core.GraphManager.GraphManager`
        """
        return self.man